En este notebook vamos a ver cómo crear un clasificador basado en una red neuronal para el dataset iris. El dataset iris es un dataset muy conocido en el mundo del machine learning y es un dataset que se utiliza para hacer pruebas de clasificación. El dataset iris contiene 150 muestras de flores iris, cada una con 4 características: longitud del sépalo, ancho del sépalo, longitud del pétalo y ancho del pétalo. Las muestras se dividen en tres especies de iris: setosa, versicolor y virginica.
Empezaremos cargando y preparando el dataset iris.
import torch
from torch import nn, optim
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
import numpy as np
# Cargamos el dataset Iris
X, y = load_iris(return_X_y=True)
# Normalizamos los datos
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)
# Lo convertimos a tensores de PyTorch
X_tensor = torch.tensor(X_scaled, dtype=torch.float32)
y_tensor = torch.tensor(y, dtype=torch.long)
# Dividimos en conjunto de entrenamiento y de test
X_train, X_test, y_train, y_test = train_test_split(X_tensor, y_tensor, test_size=0.2, random_state=42)
# Imprimimos los 10 primeros ejemplos y sus etiquetas
print(X_train[:10])
print(y_train[:10])
print("Longitud del conjunto de entrenamiento:", len(X_train))
print("Longitud del conjunto de test:", len(X_test))
tensor([[-1.5065, 1.2492, -1.5676, -1.3154], [-0.1737, 3.0908, -1.2834, -1.0522], [ 1.0380, 0.0982, 0.3649, 0.2641], [-1.2642, 0.7888, -1.2266, -1.3154], [-1.7489, 0.3284, -1.3971, -1.3154], [ 0.5533, -1.2830, 0.7059, 0.9223], [ 0.6745, 0.3284, 0.4217, 0.3958], [-0.7795, 1.0190, -1.2834, -1.3154], [-1.0218, 1.2492, -1.3402, -1.3154], [-0.7795, 2.4002, -1.2834, -1.4471]]) tensor([0, 0, 1, 0, 0, 2, 1, 0, 0, 0]) Longitud del conjunto de entrenamiento: 120 Longitud del conjunto de test: 30
Creamos el modelo de red neuronal con cinco neuronas en la capa oculta.
# Definimos el modelo
class IrisNet(nn.Module):
def __init__(self):
super(IrisNet, self).__init__()
self.fc1 = nn.Linear(4, 5) # 4 características de entrada, 5 neuronas en la capa oculta
self.fc2 = nn.Linear(5, 3) # 3 clases de salida
def forward(self, x):
x = torch.relu(self.fc1(x))
x = self.fc2(x)
return x # Fíjate en que no aplicamos la función de activación softmax, ya que la función de pérdida CrossEntropyLoss lo hace por nosotros
model = IrisNet()
print(model)
print("Número de parámetros:", sum(p.numel() for p in model.parameters()))
IrisNet( (fc1): Linear(in_features=4, out_features=5, bias=True) (fc2): Linear(in_features=5, out_features=3, bias=True) ) Número de parámetros: 43
Entrenamos el modelo.
# Definimos el criterio de pérdida y el optimizador
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=0.01)
# Entrenamos el modelo
epochs = 100
for epoch in range(epochs):
optimizer.zero_grad()
output = model(X_train) # Pasamos el conjunto de entrenamiento completo, rara vez se hace esto en la práctica
loss = criterion(output, y_train) # Date cuenta de que pasamos las etiquetas originales, no en formato one-hot
loss.backward()
optimizer.step()
if epoch % 10 == 0:
print(f'Epoch {epoch+1}, Loss: {loss.item()}')
Evaluamos el modelo.
# Evaluamos el modelo
with torch.no_grad():
correct = 0
total = y_test.size(0)
outputs = model(X_test)
_, predicted = torch.max(outputs.data, 1)
correct += (predicted == y_test).sum().item()
print(f'Accuracy of the model on the test set: {round(100 * correct / total, 2)}%')
Accuracy of the model on the test set: 96.67%
model(X_test)
tensor([[-2.6884, 1.6662, 0.1354], [ 3.0821, -2.4969, -2.7127], [-7.8466, 0.6208, 6.1597], [-2.9156, 1.4927, 0.5944], [-3.6345, 1.6372, 1.1046], [ 2.3956, -1.9155, -2.5018], [-1.5794, 1.4062, -0.7765], [-5.1603, 0.8852, 3.4350], [-4.4466, 1.8076, 1.8326], [-2.2002, 1.8953, -0.4881], [-4.0717, 0.9560, 2.2465], [ 2.3428, -1.8511, -2.5544], [ 2.9082, -2.3366, -2.7049], [ 2.4489, -1.9432, -2.5794], [ 3.3549, -2.7168, -2.8357], [-2.6529, 1.2593, 0.5293], [-5.3344, 0.6769, 3.7309], [-2.2739, 1.9836, -0.5126], [-2.5004, 1.6002, 0.0594], [-5.5386, 0.8060, 3.8413], [ 2.5278, -2.0166, -2.5806], [-3.6611, 1.1528, 1.6581], [ 2.5048, -2.0087, -2.5330], [-5.3313, 0.8670, 3.5614], [-4.6422, 0.5316, 3.0221], [-5.2905, 0.8549, 3.5914], [-5.6369, 1.1563, 3.5222], [-5.4068, 0.5569, 3.9078], [ 2.0617, -1.6234, -2.4320], [ 2.3044, -1.8273, -2.5121]], grad_fn=<AddmmBackward0>)
En PyTorch, with torch.no_grad():
es un contexto que se utiliza para desactivar el cálculo y almacenamiento de gradientes que normalmente se realiza en operaciones sobre tensores. Esto es útil en situaciones donde no necesitas realizar backpropagation, es decir, no necesitas calcular los gradientes para la optimización de los parámetros del modelo. Esto suele hacerse durante la fase de evaluación o inferencia del modelo, cuando solo estás realizando predicciones o evaluando el rendimiento del modelo y no quieres modificar sus parámetros.
Es muy común realizar evaluaciones del modelo durante su entrenamiento, por lo que es importante desactivar el cálculo de gradientes en estas situaciones para evitar que se acumulen en la memoria y ralenticen el proceso.
epochs = 100
for epoch in range(epochs):
optimizer.zero_grad()
output = model(X_train)
loss = criterion(output, y_train) # Date cuenta de que pasamos las etiquetas originales, no en formato one-hot
loss.backward()
optimizer.step()
if epoch % 10 == 0:
print(f'Epoch {epoch+1}, Loss: {loss.item()}')
# Evaluamos el modelo con el conjunto de test
with torch.no_grad():
correct = 0
total = y_test.size(0)
outputs = model(X_test)
_, predicted = torch.max(outputs.data, 1)
correct += (predicted == y_test).sum().item()
print(f'Accuracy of the model on the test set: {round(100 * correct / total, 2)}%')
Epoch 1, Loss: 1.1203858852386475 Accuracy of the model on the test set: 23.33% Epoch 11, Loss: 0.9088758230209351 Accuracy of the model on the test set: 63.33% Epoch 21, Loss: 0.7178919315338135 Accuracy of the model on the test set: 93.33% Epoch 31, Loss: 0.5924195051193237 Accuracy of the model on the test set: 93.33% Epoch 41, Loss: 0.49193066358566284 Accuracy of the model on the test set: 86.67% Epoch 51, Loss: 0.419420063495636 Accuracy of the model on the test set: 86.67% Epoch 61, Loss: 0.36532923579216003 Accuracy of the model on the test set: 86.67% Epoch 71, Loss: 0.3160657584667206 Accuracy of the model on the test set: 93.33% Epoch 81, Loss: 0.27905404567718506 Accuracy of the model on the test set: 93.33% Epoch 91, Loss: 0.24549810588359833 Accuracy of the model on the test set: 93.33%
! pip install torchsummary
Requirement already satisfied: torchsummary in /Users/cayetano/Propio/Notebooks/Machine Learning/RL/env/lib/python3.10/site-packages (1.5.1) [notice] A new release of pip is available: 23.3.1 -> 24.0 [notice] To update, run: pip install --upgrade pip
from torchsummary import summary
summary(model, (4,))
---------------------------------------------------------------- Layer (type) Output Shape Param # ================================================================ Linear-1 [-1, 5] 25 Linear-2 [-1, 3] 18 ================================================================ Total params: 43 Trainable params: 43 Non-trainable params: 0 ---------------------------------------------------------------- Input size (MB): 0.00 Forward/backward pass size (MB): 0.00 Params size (MB): 0.00 Estimated Total Size (MB): 0.00 ----------------------------------------------------------------
summary(model, (4,), batch_size=10)
---------------------------------------------------------------- Layer (type) Output Shape Param # ================================================================ Linear-1 [10, 5] 25 Linear-2 [10, 3] 18 ================================================================ Total params: 43 Trainable params: 43 Non-trainable params: 0 ---------------------------------------------------------------- Input size (MB): 0.00 Forward/backward pass size (MB): 0.00 Params size (MB): 0.00 Estimated Total Size (MB): 0.00 ----------------------------------------------------------------